package ws.jonas.sybosc.gui;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;

import org.jfree.chart.JFreeChart;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.TimeSeriesDataItem;

import ws.jonas.common.model.Product;
import ws.jonas.common.model.ProductQuantity;
import ws.jonas.common.model.Store;
import ws.jonas.common.web.WebQuerier;
import ws.jonas.sybosc.chart.ChartUtil;
import ws.jonas.sybosc.db.DBConnection;
import ws.jonas.sybosc.model.Average;
import ws.jonas.sybosc.model.StockTableObject;

/**
 * TODO: Add Counties/"Storstäder" to search options
 */
@SuppressWarnings("serial")
public class SyboscPanel extends JPanel {
	private final JPanel northPane;
	private final JPanel resultPane;
	private final JPanel searchPane;
	private final JButton searchButton;
	private final JButton searchLiveButton;

	private final List<SearchOption<?>> searchOptions;
	private final SearchOption<String> productsByName;
	private final SearchOption<Integer> productsByNr;
	private final SearchOption<String> fromDate;
	private final SearchOption<String> toDate;
	private final SearchOption<String> cities;
	private final SearchOption<String> counties;

	private final DBConnection conn;

	public SyboscPanel(final DBConnection conn) {
		setLayout(new BorderLayout());
		this.conn = conn;

		searchOptions = new LinkedList<SearchOption<?>>();

		productsByName = new SearchOption.MultiSelectionListOption<String>("Products", new SearchOption.MultiSelectionListOption.MultiSelectionListWorker<String>() {
			@Override
			public String[] getNewValues() {
				try {
					List<Product> products = conn.getDb().getProducts();
					String[] productNames = new String[products.size()];
					for (int i = 0; i < products.size(); ++i) {
						productNames[i] = products.get(i).getName();
					}
					return productNames;
				} catch (SQLException e) {
					e.printStackTrace(); //TODO:
				}
				return null;
			}
		});
		searchOptions.add(productsByName);

		productsByNr = new SearchOption.MultiSelectionListOption<Integer>("Products", new SearchOption.MultiSelectionListOption.MultiSelectionListWorker<Integer>() {
			@Override
			public Integer[] getNewValues() {
				try {
					List<Product> products = conn.getDb().getProducts();
					Integer[] productNrs = new Integer[products.size()];
					for (int i = 0; i < products.size(); ++i) {
						productNrs[i] = products.get(i).getProdnr();
					}
					return productNrs;
				} catch (SQLException e) {
					e.printStackTrace(); //TODO:
				}
				return null;
			}
		});
		searchOptions.add(productsByNr);

		fromDate = new SearchOption.DateOption("From", new SearchOption.DateOption.DateOptionWorker() {
			@Override
			public String getNewValue() {
				try {
					String date = conn.getDb().getDateInterval()[0];
					return date != null ? date : "";
				} catch (SQLException e) {
					e.printStackTrace(); //TODO
				}
				return "";
			}
		});
		searchOptions.add(fromDate);

		toDate = new SearchOption.DateOption("To", new SearchOption.DateOption.DateOptionWorker() {
			@Override
			public String getNewValue() {
				try {
					String date = conn.getDb().getDateInterval()[1];
					return date != null ? date : "";
				} catch (SQLException e) {
					e.printStackTrace(); //TODO
				}
				return "";
			}
		});
		searchOptions.add(toDate);

		cities = new SearchOption.MultiSelectionListOption<String>("City", new SearchOption.MultiSelectionListOption.MultiSelectionListWorker<String>() {
			@Override
			public String[] getNewValues() {
				try {
					List<String> cities = conn.getDb().getDistinctCities();
					//cities.add(0, "Storstäder");
					return cities.toArray(new String[cities.size()]);
				} catch (SQLException e) {
					e.printStackTrace(); //TODO
				}
				return null;
			}
		});
		searchOptions.add(cities);

		counties = new SearchOption.MultiSelectionListOption<String>("County", new SearchOption.MultiSelectionListOption.MultiSelectionListWorker<String>() {
			@Override
			public String[] getNewValues() {
				try {
					List<String> counties = conn.getDb().getDistinctCounties();
					return counties.toArray(new String[counties.size()]);
				} catch (SQLException e) {
					e.printStackTrace(); // TODO: handle exception
				}
				return null;
			}
		});
		searchOptions.add(counties);

		searchPane = new JPanel();
		for (SearchOption<?> option : searchOptions) {
			if (option != null) {
				searchPane.add(option);
			}
		}

		searchButton = new JButton("Search");
		searchButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				//TODO: remove stdout
				System.out.println("SyboscPanel.SyboscPanel(...).new ActionListener() {...}.actionPerformed()");
				System.out.println(productsByName.getMultipleSelections());
				System.out.println(productsByNr.getMultipleSelections());
				System.out.println(fromDate.getSingleSelection());
				System.out.println(toDate.getSingleSelection());
				System.out.println(cities.getMultipleSelections());

				if (conn.isOnline()) {
					Worker.execute(new Runnable() {
						@Override
						public void run() {
							try {
								Map<Product, List<StockTableObject>> stocksByProduct =
										conn.getDb().getStocks(productsByName.getMultipleSelections(),
												productsByNr.getMultipleSelections(),
												cities.getMultipleSelections(), counties.getMultipleSelections(),
												fromDate.getSingleSelection(), toDate.getSingleSelection());

								TimeSeriesCollection collection = new TimeSeriesCollection();
								for (Map.Entry<Product, List<StockTableObject>> entry : stocksByProduct.entrySet()) {
									Product prod = entry.getKey();
									List<StockTableObject> stocks = entry.getValue();
									TimeSeries series = new TimeSeries(prod.getUniqueName());
									Map<Day, Average> averageByDay = new HashMap<Day, Average>();
									for (StockTableObject stock : stocks) {
										Day day = new Day(stock.getInsertTime());
										int qty = stock.getQuantity();

										Average average = averageByDay.get(day);
										if (average == null) {
											average = new Average(qty);
											averageByDay.put(day, average);
										} else {
											average.add(qty);
										}
									}

									for (Map.Entry<Day, Average> entry2 : averageByDay.entrySet()) {
										TimeSeriesDataItem item = new TimeSeriesDataItem(entry2.getKey(), entry2.getValue().average());
										series.add(item);
									}
									collection.addSeries(series);
								}
								updateResult(ChartUtil.createTimeChart("Search result", "xLabel", "yLabel", collection));
							} catch (SQLException e) {
								e.printStackTrace(); //TODO
							}
						}
					});
				} //TODO: else?
			}
		});

		searchLiveButton = new JButton("Live Search");
		searchLiveButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Worker.execute(new Runnable() {
					@Override
					public void run() {
						System.out.println("SyboscPanel.SyboscPanel(...).new ActionListener() {...}.actionPerformed()");
						List<ProductQuantity> products = WebQuerier.query("2755");
						DefaultCategoryDataset dataset = new DefaultCategoryDataset();
						int i = 0;
						for (ProductQuantity product : products) {
							for (Map.Entry<Store, Integer> entry : product) {
								int quantity = entry.getValue();
								if (quantity > 0) {
									dataset.addValue(quantity, entry.getKey().getCity(), product.getProduct().getVolume());
									if (i++ > 10) {
										break;
									}
								}
							}
						}

						JFreeChart chart = ChartUtil.createBarChart("Test title", "xlabel", "ylabel", dataset);
						updateResult(chart);
					}
				});
			}
		});

		JLabel textArea2 = new JLabel("Search results");
		resultPane = new JPanel();
		resultPane.add(textArea2);

		northPane = new JPanel();
		northPane.setLayout(new BoxLayout(northPane, BoxLayout.PAGE_AXIS));
		northPane.add(searchPane);
		northPane.add(searchButton);
		northPane.add(searchLiveButton);
		northPane.add(new JSeparator(SwingConstants.HORIZONTAL));

		add(northPane, BorderLayout.PAGE_START);
		add(resultPane, BorderLayout.CENTER);
	}

	public void onConnect() {
		if (!conn.isOnline()) {
			return;
		}
		for (SearchOption<?> option : searchOptions) {
			if (option != null) {
				option.refresh();
			}
		}
	}

	public void onDisconnect() {

	}

	public void updateResult(JFreeChart chart) {
		if (chart == null) {
			throw new NullPointerException("chart");
		}

		resultPane.removeAll();
		resultPane.add(ChartUtil.createChartPanel(chart));
		resultPane.validate();
		resultPane.repaint();
	}

	private static class Worker {
		private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();

		private Worker() {
			throw new UnsupportedOperationException();
		}

		private static void execute(Runnable runnable) {
			EXECUTOR.execute(runnable);
		}
	}
}
